<!---
Copyright: (c) 2006 The MachBlog Authors
Authors: Matt Woodward (mpwoodward@gmail.com) & Peter J. Farrell (pjf@maestropublishing.com)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. 
You may obtain a copy of the License at 

http://www.apache.org/licenses/LICENSE-2.0 

Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
See the License for the specific language governing permissions and 
limitations under the License.

$Id: EntryListener.cfc 1187 2007-07-07 02:40:10Z pfarrell $

Notes:
This listener is autowired by ColdSpring.
TODO: add category service so we aren't accessing category stuff directly
--->
<cfcomponent
	displayname="EntryListener"
	extends="MachII.framework.Listener"
	output="false" 
	hint="EntryListener for MachBlog">

	<!---
	INITIALIZATION / CONFIGURATION
	--->
	<cffunction name="configure" access="public" output="false" returntype="void" 
		hint="Configures this listener as part of the Mach-II framework.">
		<!--- Does nothing. --->
	</cffunction>

	<cffunction name="setEntryService" access="public" output="false" returntype="void"
			hint="Dependency: injected">
		<cfargument name="entryService" type="org.machblog.entry.EntryService" required="true" />
		<cfset variables.entryService = arguments.entryService />
	</cffunction>	
	<cffunction name="getEntryService" access="public" output="false" returntype="org.machblog.entry.EntryService">
		<cfreturn variables.entryService />
	</cffunction>
	
	<cffunction name="setCategoryService" access="public" output="false" returntype="void" 
			hint="Dependency: injected">
		<cfargument name="categoryService" type="org.machblog.category.CategoryService" required="true" />
		<cfset variables.categoryService = arguments.categoryService />
	</cffunction>
	<cffunction name="getCategoryService" access="public" output="false" returntype="org.machblog.category.CategoryService">
		<cfreturn variables.categoryService />
	</cffunction>
	
	<cffunction name="setAggregatorService" access="public" output="false" returntype="void" 
			hint="Dependency: injected">
		<cfargument name="aggregatorService" type="org.machblog.aggregator.AggregatorService" required="true" />
		<cfset variables.aggregatorService = arguments.aggregatorService />
	</cffunction>
	<cffunction name="getAggregatorService" access="public" output="false" returntype="org.machblog.aggregator.AggregatorService">
		<cfreturn variables.aggregatorService />
	</cffunction>
	
	<cffunction name="setNotificationService" access="public" output="false" returntype="void" 
			hint="Dependency: injected">
		<cfargument name="notificationService" type="org.machblog.notification.NotificationService" 
				required="true" />
		<cfset variables.notificationService = arguments.notificationService />
	</cffunction>
	<cffunction name="getNotificationService" access="public" output="false" 
			returntype="org.machblog.notification.NotificationService">
		<cfreturn variables.notificationService />
	</cffunction>
	
	<cffunction name="setUdfs" access="public" output="false" returntype="void">
		<cfargument name="udfs" type="org.machblog.utils.udfs" required="true" />
		<cfset variables.udfs = arguments.udfs />
	</cffunction>
	<cffunction name="getUdfs" access="public" output="false" returntype="org.machblog.utils.udfs">
		<cfreturn variables.udfs />
	</cffunction>
	
	<!---
	PUBLIC FUNCTIONS - GATEWAY RELATED
	--->
	<cffunction name="getEntriesForHomePage" access="public" output="false" returntype="query" 
			hint="Gets the entries for the home page.">
		<cfreturn getEntryService().getEntriesForHomePage(numDays = getProperty("numDaysOnHomePage")) />
	</cffunction>
	
	<cffunction name="getAllEntries" access="public" output="false" returntype="query" 
			hint="Returns a query object containing all blog entries (active and inactive).">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		<cfreturn getEntryService().getAllEntries(searchTerms = arguments.event.getArg("searchTerms", "")) />
	</cffunction>
	
	<cffunction name="getEntriesByCategory" access="public" output="false" returntype="query" 
			hint="Returns a query object containing blog entries for a particular category.">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		<cfreturn getEntryService().getEntriesByCategory(arguments.event.getArg("categoryId")) />
	</cffunction>
	
	<cffunction name="getEntriesByMonth" access="public" output="false" returntype="query" 
			hint="Returns a query object containing blog entries for a particular month and year">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		<cfreturn getEntryService().getEntriesByMonth(arguments.event.getArg("month"), arguments.event.getArg("year")) />
	</cffunction>
	
	<cffunction name="searchEntries" access="public" output="false" returntype="query" 
			hint="Returns a query object containing entries that meet the search criteria.">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		<cfreturn getEntryService().searchEntries(arguments.event.getArg("searchTerms")) />
	</cffunction>
	
	<cffunction name="getEntryCount" access="public" output="false" returntype="numeric" 
			hint="Returns the total number of entries in the blog">
		<cfreturn getEntryService().getEntryCount() />
	</cffunction>
	
	<cffunction name="getCurrentUserEntryCount" access="public" output="false" returntype="numeric" 
			hint="Returns the number of entries for the logged-in user">
		<cfreturn getEntryService().getCurrentUserEntryCount() />
	</cffunction>
	
	<cffunction name="getArchiveList" access="public" output="false" returntype="array" 
			hint="Returns an array containing the month start epoch and total entries for the month. Uses the numMonthsOfArchives property.">
		<cfreturn getEntryService().getArchiveList(getProperty("numMonthsOfArchives")) />
	</cffunction>
	
	<cffunction name="incrementViews" access="public" output="false" returntype="void" 
			hint="Increments the number of times the entry has been viewed">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		
		<cfset getEntryService().incrementViews(arguments.event.getArg("entryId")) />
	</cffunction>
	
	<cffunction name="getEntryTitleById" access="public" output="false" returntype="string" 
			hint="Returns the entry title for the entry ID passed in">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		
		<cfreturn getEntryService().getEntryTitleById(arguments.event.getArg("entryId")) />
	</cffunction>
	
	<cffunction name="processSendEntryToFriend" access="public" output="false" returntype="void" 
			hint="Sends an entry to an email address and announces the next event">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		
		<cfset var emailBody = trim(arguments.event.getArg("comments")) />
		<cfset var emailSubject = getProperty("resourceBundleService").getResourceBundle().getResource("blogpostfrom") 
									& " " & getProperty("blogHeaderTitle") & ": " 
									& arguments.event.getArg("entryTitle") />
		<cfset var moduleName = "" />
		<cfset var am = getAppManager() />
		
		<cfif StructKeyExists(am, "getModuleName")>
			<cfset moduleName = am.getModuleName() />
		</cfif>
		
		<cfif emailBody NEQ "">
			<cfset emailBody = emailBody & Chr(10) & Chr(13) & Chr(10) & Chr(13) />
		</cfif>
		
		<cfset emailBody = emailBody & variables.udfs.buildUrl(moduleName, 'showEntry', 'entryId=' & arguments.event.getArg("entryId")) />	
		
		<cfif getProperty("useCaptcha") AND arguments.event.isArgDefined("captchaMessage")>
			<cfset arguments.event.setArg("message", getProperty("resourceBundleService").getResourceBundle().getResource("captchafail")) />
		<cfelse>
			<cfset getNotificationService().emailEntryToFriend(arguments.event.getArg("toEmail"),
															arguments.event.getArg("fromEmail"), 
															emailSubject, emailBody) />
			<cfset arguments.event.setArg("message", getProperty("resourceBundleService").getResourceBundle().getResource("emailsent")) />
		</cfif>
		
		<cfset announceEvent("showSendEntryToFriend", arguments.event.getArgs()) />
	</cffunction>
	
	<!---
	PUBLIC FUNCTIONS - DAO RELATED
	--->
	<cffunction name="getEntry" access="public" output="false" returntype="org.machblog.entry.Entry"
		hint="Retrieves an entry by ID.">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		
		<cfreturn getEntryService().getEntryById(arguments.event.getArg("entryId", "")) />
	</cffunction>
	
	<cffunction name="processBlogEntryForm" access="public" output="false" returntype="void"
		hint="Processes the blog entry form and announces the next event.">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		
		<cfset var entry = arguments.event.getArg("entry") />
		<cfset var categories = ArrayNew(1) />
		<cfset var category = CreateObject("component", "org.machblog.category.Category").init() />
		<cfset var newCat = "" />
		<cfset var subscribers = arguments.event.getArg("subscribers") />
		<cfset var results = StructNew() />
		<cfset var photoFile = "" />
		<cfset var enclosureFile = "" />
		<cfset var enclosureMimeTypeId = "" />
		<cfset var enclosureSize = "" />
		<cfset var dateToPost = 0 />
		<cfset var timeToPost = 0 />
		<cfset var dtToPost = 0 />
		<cfset var archiveList = "" />
		<cfset var i = 0 />

		<cfset results.success = true />
		
		<!--- handle additional form arguments that couldn't be handled with the event bean --->
		<cfset entry.setDtPosted(getProperty("resourceBundleService").getLocaleUtils().i18nDateTimeParse(arguments.event.getArg("dtPosted"))) />
		
		<cfif arguments.event.isArgDefined("isActive")>
			<cfset entry.getAudit().setIsActive(arguments.event.getArg("isActive")) />
		</cfif>
		
		<cfif arguments.event.getArg("category") NEQ "">
			<cfset categories = ListToArray(arguments.event.getArg("category"), ",") />
			<cfset entry.setCategories(categories) />
		</cfif>
		
		<!--- upload/delete photo and enclosure as needed --->
		<cfif arguments.event.getArg("photo") NEQ "">
			<cftry>
				<cffile action="upload" filefield="photo" destination="#ExpandPath('uploads/photos/')#" 
						accept="image/*" nameconflict="makeunique" />
				<!--- if the upload was successful set the photo info to the bean --->
				<cfif CFFILE.FileWasSaved>
					<cfset entry.setPhoto(CFFILE.ServerDirectory & "/" & CFFILE.ServerFile) />
				</cfif>
				<cfcatch type="any">
					<cfset results.success = false />
					<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("errormessagefileupload") & CFCATCH.Detail />
				</cfcatch>
			</cftry>
		<cfelseif arguments.event.isArgDefined("deletePhoto") AND arguments.event.getArg("deletePhoto")>
			<cfset getEntryService().deleteFile(ExpandPath("uploads/photos/" & arguments.event.getArg("oldPhoto"))) />
		</cfif>
		
		<cfif arguments.event.getArg("enclosure") NEQ "">
			<cftry>
				<cffile action="upload" filefield="enclosure" destination="#ExpandPath('uploads/enclosures/')#" 
						nameconflict="makeunique" />
				<!--- if the enclosure was uploaded set the other enclosure variables --->
				<cfif CFFILE.FileWasSaved>
					<cfset entry.setEnclosure(CFFILE.ServerDirectory & "/" & CFFILE.ServerFile) />
					<cfset entry.setEnclosureMimeType(CFFILE.ContentType & "/" & CFFILE.ContentSubtype) />
					<cfset entry.setEnclosureSize(CFFILE.FileSize) />
				</cfif>
				<cfcatch type="any">
					<cfset results.success = false />
					<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("errormessagefileupload") & CFCATCH.Detail />
				</cfcatch>
			</cftry>
		<cfelseif arguments.event.isArgDefined("deleteEnclosure") AND arguments.event.getArg("deleteEnclosure")>
			<cftry>
				<cfset getEntryService().deleteFile(ExpandPath("uploads/enclosures/" 
																	& arguments.event.getArg("oldEnclosure"))) />
				<cfcatch type="any">
					<cfset results.success = false />
					<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("errormessagefiledelete") & CFCATCH.Detail />
				</cfcatch>
			</cftry>
		</cfif>
		
		<!--- if no errors occurred, proceed --->
		<cfif results.success>
			<!--- deal with the categories --->
			<cfif arguments.event.getArg("newCategory") NEQ "">
				<cfloop index="newCat" list="#arguments.event.getArg('newCategory')#" delimiters=",">
					<cfset category = getCategoryService().getCategoryBean() />
					<cfset category.setCategoryId(CreateUUID()) />
					<cfset category.setCategoryName(newCat) />
					<cfset category.getAudit().setCreatedById(getEntryService().getUserFacade().getUser().getUserId()) />
					<cfset category.getAudit().setDtCreated(getProperty("resourceBundleService").getLocaleUtils().toEpoch(Now())) />
					<cfset category.getAudit().setIpCreated(CGI.REMOTE_ADDR) />
					<cfset category.getAudit().setIsActive(1) />
					<cfset getCategoryService().createCategory(category) />
					<cfset ArrayAppend(categories, category.getCategoryId()) />
				</cfloop>
				
				<cfset entry.setCategories(categories) />
			</cfif>
			
			<cfif entry.getEntryId() EQ "">
				<cfset entry.setEntryId(CreateUUID()) />
				<cfset entry.getAudit().setCreatedById(getEntryService().getUserFacade().getUser().getUserId()) />
				<cfset entry.getAudit().setDtCreated(getProperty("resourceBundleService").getLocaleUtils().toEpoch(Now())) />
				<cfset entry.getAudit().setIpCreated(CGI.REMOTE_ADDR) />
				<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("entrycreatesuccessmessage") />
				
				<cftry>
					<cfset getEntryService().createEntry(entry) />
					<cfcatch type="any">
						<cfset results.success = false />
						<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("entrycreatefailedmessage") & CFCATCH.Detail />
					</cfcatch>
				</cftry>
				
				<cfset emailSubject = getProperty('resourceBundleService').getResourceBundle().getResource('newentryemailsubject') />
			<cfelse>
				<!--- set the photo value if an old one exists so it doesn't get nuked on an update --->
				<cfif event.getArg("oldPhoto") is not "">
					<cfset entry.setPhoto(event.getArg("oldPhoto")) />
				</cfif>
				<cfset entry.getAudit().setModifiedById(getEntryService().getUserFacade().getUser().getUserId()) />
				<cfset entry.getAudit().setDtModified(getProperty("resourceBundleService").getLocaleUtils().toEpoch(Now())) />
				<cfset entry.getAudit().setIpModified(CGI.REMOTE_ADDR) />
				<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("entryupdatesuccessmessage") />
				
				<cftry>
					<cfset getEntryService().updateEntry(entry) />
					<cfcatch type="any">
						<cfset results.success = false />
						<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("entryupdatefailedmessage") & CFCATCH.Detail />
					</cfcatch>
				</cftry>
				
				<cfset emailSubject = getProperty('resourceBundleService').getResourceBundle().getResource('updatedentryemailsubject') />
			</cfif>
		</cfif>

		<cfset arguments.event.setArg("message", results.message) />
		
		<cfif Not results.success>
			<cfset announceEvent("showBlogEntryForm_error", arguments.event.getArgs()) />
		<cfelse>
			<!--- if this is entry is going live now, notify subscribers of new entries and updates 
					and ping the aggregators --->
			<cfif entry.getAudit().getIsActive() AND 
					entry.getDtPosted() LTE getProperty("resourceBundleService").getLocaleUtils().toEpoch(Now())>
				<cfif subscribers.RecordCount GT 0>
					<cfset getEntryService().notifySubscribers(entry.getEntryId(), entry.getTitle(), 
																subscribers, emailSubject, getProperty("emailAuthor"), 
																getProperty("blogHeaderTitle"), getProperty("blogUrl"), 
																getProperty("resourceBundleService").getResourceBundle().getResource("unsubscribemessage"), 
																getProperty("eventParameter")) />
				</cfif>

				<!--- increment the blog entry count for the appropriate month --->
				<cfset archiveList = getProperty("archiveList") />
				
				<cfloop index="i" from="1" to="#arrayLen(archiveList)#" step="1">
					<cfif getProperty("resourceBundleService").getLocaleUtils().getMonth(archiveList[i][1]) eq
							getProperty("resourceBundleService").getLocaleUtils().getMonth(entry.getDtPosted()) and 
							getProperty("resourceBundleService").getLocaleUtils().getYear(archiveList[i][1]) eq
							getProperty("resourceBundleService").getLocaleUtils().getYear(entry.getDtPosted())>
						<cfset archiveList[i][2] = archiveList[i][2] + 1 />
					</cfif>
				</cfloop>				

				<cfset setProperty("archiveList", archiveList) />

				<!--- ping aggregators --->
				<cfset getAggregatorService().pingAggregators() />
			</cfif>
			
			
			
			<!---<cfset announceEvent("showBlogEntryAdmin_redirect", arguments.event.getArgs()) />--->
			<cfset announceEvent("showBlogEntryAdmin_redirect", arguments.event.getArgs()) />
		</cfif>
	</cffunction>
	
	<cffunction name="deleteBlogEntry" access="public" output="false" returntype="void" 
		hint="Deletes a blog entry and announces the next event">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		
		<cfset var entry = getEntryService().getEntryBean().init(arguments.event.getArg("entryId")) />
		<cfset var results = StructNew() />

		<cfset results.success = true />
		<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("entrydeletesuccessmessage") />
		
		<cftry>
			<cfset getEntryService().deleteEntry(entry) />
			<cfcatch type="any">
				<cfset results.success = false />
				<cfset results.message = getProperty("resourceBundleService").getResourceBundle().getResource("entrydeletefailedmessage") />
			</cfcatch>
		</cftry>
		
		<cfset arguments.event.setArg("message", results.message) />
		
		<cfif results.success>
			<cfset announceEvent("deleteBlogEntrySucceeded", arguments.event.getArgs()) />
		<cfelse>
			<cfset announceEvent("showBlogEntryAdmin", arguments.event.getArgs()) />
		</cfif>
	</cffunction>
	
	<cffunction name="notifySubscribers" access="public" output="false" returntype="void" 
			hint="Notifies subscribers of blog entries that were set for delayed posting">
		<cfargument name="event" type="MachII.framework.Event" required="true" />
		
		<cfset var subscribers = arguments.event.getArg("subscribers") />
		<cfset var entries = getEntryService().getUnnotifiedEntries() />
		<cfset var emailSubject = getProperty('resourceBundleService').getResourceBundle().getResource('newentryemailsubject') />
		
		<cfif entries.RecordCount GT 0>
			<cfloop query="entries">
				<cfset getEntryService().notifySubscribers(entries.entry_id, entries.title, 
															subscribers, emailSubject, 
															getProperty("emailAuthor"), 
															getProperty("blogHeaderTitle"), 
															getProperty("blogUrl"), 
															getProperty("resourceBundleService").getResourceBundle().getResource("unsubscribemessage"), 
															getProperty("smtpMailerId"), 
															getProperty("smtpServer"), 
															getProperty("eventParameter")) />
			</cfloop>
		</cfif>
	</cffunction>
</cfcomponent>